//
//  DPTestCase.h
//  DPUnitTest
//
//  Created by Ofri Wolfus on 06/10/06.
//  Copyright 2006 Ofri Wolfus. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <UITestingKit/DPMacros.h>


typedef enum {
	DPTestFailed = NO,	// == 0
	DPTestSucceeded = YES,	// == 1
	DPTestPending
} DPTestStatus;


@class DPUIElement;

/*!
 * @abstract An application element of the application being tested.
 * @discussion When launching TestsRunner with the -a flag, this will
 * be the launched application's element.
 */
DP_EXTERN DPUIElement *DPTestSubject;


/*!
 * @abstract A base class for unit tests.
 * @discussion In order to use this unit test framework, you must subclass this class (or DPSpeedTest, see below).
 * Any instance method implemented by a subclass, and that its name begins with "test", will automatically be called at runtime.
 * If the return type of a test method is <code>BOOL</code>, it's assumed to be the result of the test (failure/success).
 * In case of a failure, either an exception will be raised, or it'll be logged (see the docs for -throwsForFaildTests).
 */
@interface DPTestCase : NSObject {
	@private
	int32_t _refCount;
	NSMutableArray *_tests;
	unsigned _nextTest;
	NSMutableDictionary *_pendingTests;
	id _module;
	BOOL _completed;
}

/*!
 * @abstract A generic verification method.
 * @discussion This method is being called by the testing runtime in order to verify
 * tests without a dedicated verification method.
 * Override this method in order to verify several tests that should have the same results.
 * The default implementation return <code>DPTestPending</code>, meaning the test case
 * will not be able to complete until all tests are specifically verified.
 *
 * @param result The result of the test method.
 * @param testName The selector of the test that needs verification.
 */
- (DPTestStatus)verifyResult:(id)result ofTest:(NSString *)testName;

/*!
 * @abstract Returns the number of iterations each test is executed.
 * @discussion The default implementation returns 1.
 */
- (unsigned)numberOfIterations;

/*!
 * @abstract A method indicating whether all iterations of a test
 * should be verified.
 *
 * @result If <code>YES</code> every test is verified by its matching verification function,
 * otherwise only the first test is verified.
 * The default implementation returns <code>NO</code>.
 */
- (BOOL)verifiesAllTests;

/*!
 * @abstract Sets the termination status of a pending test.
 * @discussion When a test returns nothing (void) or its verification function
 * returned <code>DPTestPending</code>, the test is added to the pending tests stack.
 * Pending tests do not allow the test case to complete until their termination
 * status is determined.
 * The ability not to verify tests immediately allows you to write tests for async
 * actions such as GUI interaction and multi-threaded API.
 */
 - (void)setStatus:(DPTestStatus)st forPendingTest:(NSString *)testName;


/*!
 * @abstract Returns whether the receiver completed all of its tests successfully or there
 * are still unfinished tests left.
 */
- (BOOL)isValid;

@end


/*!
 * @abstract A notification that gets posted when a test case completes.
 * @discussion This notification is posted when all tests have been executed and verified.
 */
DP_EXTERN NSString * const DPTestCaseCompletedNotification;

/*!
 * @abstract Posted after a failed test.
 * @discussion The object of the notification is the test case owning the failed test.
 * The user info of the notification contains one key - <code>@"statistics"</code> - holding
 * the statistics object for the failed test.
 */
DP_EXTERN NSString * const DPTestFailedNotification;


/*!
 * @abstract The name of the exception thrown unpon test failure.
 * @discussion NOTE: Other exceptions may be thrown as a side effect of the test's body,
 * which their name will not be DPFailedTestException.
 */
DP_EXTERN NSString * const DPFailedTestException;

/*!
 * @abstract An assertion macro for test cases.
 * @discussion Use this macro to verify test conditions.
 * When <code>condition</code> evaluates to <code>NO</code>, UITestingKit will
 * grab info about the failed condition, and handle the failed test.
 *
 * @param condition The condition that must be met.
 * @param desc A format string describing the reason this assertion failed.
 */
#define DPTestAssert(condition, desc...) \
do {\
	if (!(condition)) { \
		[[NSException exceptionWithName:DPFailedTestException \
								 reason:[NSString stringWithFormat:desc] \
							   userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithUTF8String:#condition], \
								   DPFailReasonKey, [NSString stringWithUTF8String:__FILE__], @"File", \
								   [NSString stringWithFormat:@"%d", __LINE__], @"Line", nil]] raise]; \
	} \
} while (0)
